import io
import os
import zipfile
import tempfile
import time
from src.db import dbm, dbs
from src.core.variables.constant import CONF
from src.core.variables.function import GUESS_LANGUAGE
from src.schema.hosting import file_change_schema, look_file_content_schema
from src.core.variables.status import SUCCESS_CODE_CUSTOMIZE, ERROR_CODE_CUSTOMIZE


def __inspect_file_in_memory(file_bytes):
    """从内存中的JAR文件字节数据获取目录结构，返回嵌套字典"""
    result = {}

    with zipfile.ZipFile(io.BytesIO(file_bytes)) as app_file:
        for name in app_file.namelist():
            parts = [p for p in name.split("/") if p]
            current_level = result

            for i, part in enumerate(parts):
                is_file = i == len(parts) - 1 and not name.endswith("/")

                if part not in current_level:
                    current_level[part] = {
                        "type": "file" if is_file else "dir",
                        "value": None if is_file else {},
                        "title": part,
                        "path": "/".join(parts[: i + 1]),
                        "guess": "" if not is_file else GUESS_LANGUAGE(part),
                    }

                if is_file:
                    try:
                        with app_file.open(name) as file:
                            content = file.read()
                            try:
                                current_level[part]["value"] = content.decode("utf-8")
                            except UnicodeDecodeError:
                                current_level[part]["value"] = None
                    except Exception as e:
                        current_level[part]["value"] = f"<Error reading file: {str(e)}>"
                else:
                    current_level = current_level[part]["value"]

    return result


def __transform_to_tree_format(
    data: dict, parent_key: str = "", counter: list = [0]
) -> dict:
    """将原始数据转换为树形格式，确保key全局唯一"""
    if not isinstance(data, dict):
        return {}

    result = {}
    for key, node in data.items():
        counter[0] += 1
        current_key = f"{parent_key}-{counter[0]}" if parent_key else str(counter[0])

        new_node = {
            "title": node["title"],
            "key": current_key,
            "type": node["type"],
            "path": node["path"],
            "guess": node["guess"],
        }

        if node["type"] == "dir":
            children = __transform_to_tree_format(node["value"], current_key, counter)
            new_node["children"] = list(children.values())
        else:
            # 在tree模式下不保留文件内容
            new_node["value"] = "<__TODO__>"

        result[key] = new_node

    return result


def __find_node_by_path(tree: dict, path: str):
    """根据路径查找树中的节点"""
    # 使用正斜杠作为路径分隔符，确保跨平台兼容性
    parts = [p for p in path.split("/") if p]
    current_node = {"children": list(tree.values())}  # 包装根节点

    for part in parts:
        found = False
        if "children" in current_node:
            for child in current_node["children"]:
                if child["title"] == part:
                    current_node = child
                    found = True
                    break

        if not found:
            return None

    return current_node


def __update_file_content(file_path, file_path_in_file, new_content):
    """
    更新JAR文件中指定路径的文件内容
    :param file_path: JAR文件物理路径
    :param file_path_in_file: 包内文件路径
    :param new_content: 新的文件内容
    """
    # 在目标文件所在目录创建临时目录，确保与目标文件在同一文件系统
    file_dir = os.path.dirname(file_path)
    timestamp = str(int(time.time() * 1000000))  # 微秒级时间戳
    temp_dir = os.path.join(file_dir, f"temp_{timestamp}")
    os.makedirs(temp_dir, exist_ok=True)
    
    # 在临时目录中创建临时文件
    temp_path = os.path.join(temp_dir, "temp_file.zip")

    try:
        with zipfile.ZipFile(file_path, "r") as zin, zipfile.ZipFile(
            temp_path, "w", compression=zipfile.ZIP_DEFLATED
        ) as zout:

            # 复制所有未修改的文件，保留原始元数据
            for item in zin.infolist():
                if item.filename != file_path_in_file:
                    # 保留原始文件的数据和元数据
                    data = zin.read(item.filename)
                    zout.writestr(item, data)
                else:
                    # 更新修改后的文件，但保留原始元数据
                    item_new = zipfile.ZipInfo(file_path_in_file)
                    item_new.date_time = item.date_time
                    item_new.compress_type = item.compress_type
                    zout.writestr(item_new, new_content)

        # 替换原文件
        os.replace(temp_path, file_path)
        return True
    except Exception as e:
        raise e
    finally:
        # 清理临时目录
        if os.path.exists(temp_dir):
            try:
                # 删除临时文件
                if os.path.exists(temp_path):
                    os.remove(temp_path)
                # 删除临时目录
                os.rmdir(temp_dir)
            except Exception:
                # 忽略清理过程中的错误
                pass


def java(schema: look_file_content_schema):
    """处理JAR文件请求"""
    app_id = schema.app_id
    path = getattr(schema, "path", "")
    mode = getattr(schema, "mode", "tree")

    with dbm.get("hosting_Java") as db_session:
        from sqlalchemy.orm import scoped_session
        db_session: scoped_session
        db_result = (
            db_session.query(dbs.hosting_Java.App)
            .filter(dbs.hosting_Java.App.app_id == app_id)
            .first()
        )

        if not db_result:
            return ERROR_CODE_CUSTOMIZE("应用不存在！", 400)

        file_path = os.path.join(CONF.PATH.REPO, db_result.alias, db_result.time_dir, db_result.file_name)
        if not os.path.exists(file_path):
            return ERROR_CODE_CUSTOMIZE("文件不存在！", 500)

        file_name = os.path.basename(file_path)

        with open(file_path, "rb") as f:
            file_data = f.read()
            file_structure = __inspect_file_in_memory(file_data)

        if mode in ["tree", "level"]:
            full_tree = __transform_to_tree_format(file_structure)

            if path in ["", "/"]:
                response = [
                    {
                        "title": file_name,
                        "key": "1",
                        "type": "dir",
                        "path": "root",
                        "guess": "",
                        "children": list(full_tree.values()),
                    }
                ]
            else:
                target_node = __find_node_by_path(full_tree, path)
                if not target_node:
                    return ERROR_CODE_CUSTOMIZE("Path not found", 404)

                if target_node["type"] == "file":
                    response = [
                        {
                            "title": target_node["title"],
                            "key": target_node["key"],
                            "type": target_node["type"],
                            "path": path,
                            "guess": target_node.get("guess", ""),
                            "value": "<__TODO__>",
                        }
                    ]
                else:
                    response = [
                        {
                            "title": target_node["title"],
                            "key": target_node["key"],
                            "type": target_node["type"],
                            "path": path,
                            "guess": target_node.get("guess", ""),
                            "children": target_node.get("children", []),
                        }
                    ]
        else:  # value模式
            current_data = file_structure
            path_parts = [p for p in path.split("/") if p]

            for key in path_parts:
                if key not in current_data:
                    return ERROR_CODE_CUSTOMIZE("Path not found", 404)

                if current_data[key]["type"] == "dir":
                    current_data = current_data[key]["value"]
                else:
                    response = [
                        {
                            "title": path_parts[-1],
                            "key": "0",
                            "type": "file",
                            "path": path,
                            "guess": current_data[key].get("guess", ""),
                            "value": current_data[key].get("value", ""),
                        }
                    ]
                    return SUCCESS_CODE_CUSTOMIZE(response)

            response = [
                {
                    "title": path_parts[-1] if path_parts else file_name,
                    "key": "0",
                    "type": "file",
                    "path": path,
                    "guess": "",
                    "value": current_data.get("value", ""),
                }
            ]

        return SUCCESS_CODE_CUSTOMIZE(response)


def java_change(schema: file_change_schema):
    app_id = schema.app_id
    path = getattr(schema, "path", "")
    value = getattr(schema, "value", None)
    if value is not None and path:
        with dbm.get("hosting_Java") as db_session:
            from sqlalchemy.orm import scoped_session
            db_session: scoped_session
            db_result = (
                db_session.query(dbs.hosting_Java.App)
                .filter(dbs.hosting_Java.App.app_id == app_id)
                .first()
            )
            if not db_result:
                return ERROR_CODE_CUSTOMIZE("应用不存在！", 400)
            file_path = os.path.join(CONF.PATH.REPO, db_result.alias, db_result.time_dir, db_result.file_name)
            if not os.path.exists(file_path):
                return ERROR_CODE_CUSTOMIZE("文件不存在！", 500)
            try:
                __update_file_content(file_path, path, value)
                return SUCCESS_CODE_CUSTOMIZE({"message": "文件保存成功"})
            except Exception as e:
                return ERROR_CODE_CUSTOMIZE(f"文件保存失败: {str(e)}", 500)
    return ERROR_CODE_CUSTOMIZE("无效的请求", 400)


def static(schema: look_file_content_schema):
    """处理JAR文件请求"""
    app_id = schema.app_id
    path = getattr(schema, "path", "")
    mode = getattr(schema, "mode", "tree")
    
    with dbm.get('hosting_Static') as db_session:
        from sqlalchemy.orm import scoped_session
        db_session: scoped_session
        db_result = (
            db_session.query(dbs.hosting_Static.App)
            .filter(dbs.hosting_Static.App.app_id == app_id)
            .first()
        )
        if not db_result:
            return ERROR_CODE_CUSTOMIZE("应用不存在！", 400)

        file_path = os.path.join(CONF.PATH.REPO, db_result.alias, db_result.time_dir, db_result.file_name)
        if not os.path.exists(file_path):
            return ERROR_CODE_CUSTOMIZE("文件不存在！", 500)

        file_name = os.path.basename(file_path)

        with open(file_path, "rb") as f:
            file_data = f.read()
            file_structure = __inspect_file_in_memory(file_data)

        if mode in ["tree", "level"]:
            full_tree = __transform_to_tree_format(file_structure)

            if path in ["", "/"]:
                response = [
                    {
                        "title": file_name,
                        "key": "1",
                        "type": "dir",
                        "path": "root",
                        "guess": "",
                        "children": list(full_tree.values()),
                    }
                ]
            else:
                target_node = __find_node_by_path(full_tree, path)
                if not target_node:
                    return ERROR_CODE_CUSTOMIZE("Path not found", 404)

                if target_node["type"] == "file":
                    response = [
                        {
                            "title": target_node["title"],
                            "key": target_node["key"],
                            "type": target_node["type"],
                            "path": path,
                            "guess": target_node.get("guess", ""),
                            "value": "<__TODO__>",
                        }
                    ]
                else:
                    response = [
                        {
                            "title": target_node["title"],
                            "key": target_node["key"],
                            "type": target_node["type"],
                            "path": path,
                            "guess": target_node.get("guess", ""),
                            "children": target_node.get("children", []),
                        }
                    ]
        else:  # value模式
            current_data = file_structure
            path_parts = [p for p in path.split("/") if p]

            for key in path_parts:
                if key not in current_data:
                    return ERROR_CODE_CUSTOMIZE("Path not found", 404)

                if current_data[key]["type"] == "dir":
                    current_data = current_data[key]["value"]
                else:
                    response = [
                        {
                            "title": path_parts[-1],
                            "key": "0",
                            "type": "file",
                            "path": path,
                            "guess": current_data[key].get("guess", ""),
                            "value": current_data[key].get("value", ""),
                        }
                    ]
                    return SUCCESS_CODE_CUSTOMIZE(response)

            response = [
                {
                    "title": path_parts[-1] if path_parts else file_name,
                    "key": "0",
                    "type": "file",
                    "path": path,
                    "guess": "",
                    "value": current_data.get("value", ""),
                }
            ]

        return SUCCESS_CODE_CUSTOMIZE(response)
    

def static_change(schema: file_change_schema):
    """处理JAR文件请求"""
    app_id = schema.app_id
    path = getattr(schema, "path", "")
    value = getattr(schema, "value", None)
    if value is not None and path:
        with dbm.get("hosting_Static") as db_session:
            from sqlalchemy.orm import scoped_session
            db_session: scoped_session
            db_result = (
                db_session.query(dbs.hosting_Static.App)
                .filter(dbs.hosting_Static.App.app_id == app_id)
                .first()
            )
            if not db_result:
                return ERROR_CODE_CUSTOMIZE("应用不存在！", 400)
            file_path = os.path.join(CONF.PATH.REPO, db_result.alias, db_result.time_dir, db_result.file_name)
            if not os.path.exists(file_path):
                return ERROR_CODE_CUSTOMIZE("文件不存在！", 500)
            try:
                __update_file_content(file_path, path, value)
                return SUCCESS_CODE_CUSTOMIZE({"message": "文件保存成功"})
            except Exception as e:
                return ERROR_CODE_CUSTOMIZE(f"文件保存失败: {str(e)}", 500)
    return ERROR_CODE_CUSTOMIZE("无效的请求", 400)